#include "gpioinput.h"
#include <QFile>
#include <QDebug>
#include <QTextStream>
#include "mylogger.h"

#ifdef Q_OS_LINUX
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#endif

using namespace ns_yht;

GpioInput::GpioInput(QObject *parent)
    : QObject(parent)
{
    // 初始化备用轮询定时器（如果poll方式不可用）
    // m_pollingTimer = new QTimer(this);
    // connect(m_pollingTimer, &QTimer::timeout, this, &GpioInput::handleGpioEvent);
}

GpioInput::~GpioInput()
{
#ifdef Q_OS_LINUX
    // 清理所有打开的GPIO
    for (const auto &button : m_gpioButtons)
    {
        if (button.notifier)
        {
            button.notifier->setEnabled(false);
            delete button.notifier;
        }
        if (button.fd >= 0)
        {
            close(button.fd);
        }
    }
#endif
}

bool GpioInput::addGpioButton(int gpioNumber, int keyCode)
{
#ifndef Q_OS_LINUX
    Q_UNUSED(gpioNumber);
    Q_UNUSED(keyCode);
    return false;
#else
    // 导出GPIO到用户空间
    if (!exportGpio(gpioNumber))
    {
        if (GLOG)
        {
            GLOG->info("Failed to export GPIO {}", gpioNumber);
        }
        return false;
    }

    // 配置GPIO为输入模式
    if (!configureGpio(gpioNumber, "in"))
    {
        if (GLOG)
        {
            GLOG->info("Failed to configure GPIO {} as input", gpioNumber);
        }
        return false;
    }

    // 配置GPIO边缘触发（双边沿触发） 配置了这个，一次按下松开会有三对press-release
    // if (!configureGpioEdge(gpioNumber, "both"))
    // {
    //     if (GLOG)
    //     {
    //         GLOG->info("Failed to configure GPIO {} edge trigger", gpioNumber);
    //     }
    //     return false;
    // }

    // 打开GPIO值文件用于读取
    int fd = openGpioValueFile(gpioNumber);
    if (fd < 0)
    {
        if (GLOG)
        {
            GLOG->info("Failed to open GPIO {} value file", gpioNumber);
        }
        return false;
    }

    // 创建QSocketNotifier来监视GPIO值文件描述符
    auto *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);

    // 连接信号槽
    connect(notifier, &QSocketNotifier::activated,
            this, &GpioInput::handleGpioEvent);

    // 保存GPIO信息
    GpioButton button;
    button.gpioNumber = gpioNumber;
    button.keyCode = keyCode;
    button.fd = fd;
    button.notifier = notifier;

    // 读取初始值
    char buffer[10];
    lseek(fd, 0, SEEK_SET);
    ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
    if (n > 0)
    {
        buffer[n] = '\0';
        button.lastValue = atoi(buffer);
    }
    else
    {
        button.lastValue = 1; // 默认高电平（大多数按键是低电平触发）
    }

    m_gpioButtons.append(button);

    if (GLOG)
    {
        GLOG->info("Successfully added GPIO button: GPIO{} -> KEYCODE{}",
                   gpioNumber, keyCode);
    }

    return true;
#endif
}

void GpioInput::handleGpioEvent()
{
#ifndef Q_OS_LINUX
    return;
#else
    // 处理每个GPIO按钮的状态变化
    for (auto &button : m_gpioButtons)
    {
        // 读取GPIO值
        char buffer[10];
        lseek(button.fd, 0, SEEK_SET);
        ssize_t n = read(button.fd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = '\0';
            int value = atoi(buffer);

            // 检查值是否发生变化
            if (value != button.lastValue)
            {
                button.lastValue = value;

                // 低电平表示按键按下（通常按键是接地的）
                if (value == 0)
                {
                    emit keyPressed(button.keyCode);
                    if (GLOG)
                    {
                        GLOG->info("GPIO{} key pressed, keycode: {}",
                                   button.gpioNumber, button.keyCode);
                    }
                }
                else
                {
                    emit keyReleased(button.keyCode);
                    if (GLOG)
                    {
                        GLOG->info("GPIO{} key released, keycode: {}",
                                   button.gpioNumber, button.keyCode);
                    }
                }
            }
        }
    }
#endif
}

bool GpioInput::exportGpio(int gpioNumber)
{
#ifndef Q_OS_LINUX
    Q_UNUSED(gpioNumber);
    return false;
#else
    QFile exportFile("/sys/class/gpio/export");
    if (!exportFile.open(QIODevice::WriteOnly))
    {
        return false;
    }

    QTextStream out(&exportFile);
    out << gpioNumber;
    exportFile.close();

    // 等待一段时间让系统完成导出
    usleep(100000); // 100ms

    return true;
#endif
}

bool GpioInput::configureGpio(int gpioNumber, const QString &direction)
{
#ifndef Q_OS_LINUX
    Q_UNUSED(gpioNumber);
    Q_UNUSED(direction);
    return false;
#else
    QString directionFile = QString("/sys/class/gpio/gpio%1/direction").arg(gpioNumber);
    QFile file(directionFile);
    if (!file.open(QIODevice::WriteOnly))
    {
        return false;
    }

    QTextStream out(&file);
    out << direction;
    file.close();

    return true;
#endif
}

bool GpioInput::configureGpioEdge(int gpioNumber, const QString &edge)
{
#ifndef Q_OS_LINUX
    Q_UNUSED(gpioNumber);
    Q_UNUSED(edge);
    return false;
#else
    QString edgeFile = QString("/sys/class/gpio/gpio%1/edge").arg(gpioNumber);
    QFile file(edgeFile);
    if (!file.open(QIODevice::WriteOnly))
    {
        return false;
    }

    QTextStream out(&file);
    out << edge;
    file.close();

    return true;
#endif
}

int GpioInput::openGpioValueFile(int gpioNumber)
{
#ifndef Q_OS_LINUX
    Q_UNUSED(gpioNumber);
    return -1;
#else
    QString valueFile = QString("/sys/class/gpio/gpio%1/value").arg(gpioNumber);

    // 以只读和非阻塞方式打开
    int fd = open(valueFile.toLocal8Bit().constData(), O_RDONLY | O_NONBLOCK);
    return fd;
#endif
}